iT邦幫忙

2023 iThome 鐵人賽

DAY 12
0
Software Development

FastAPI 開發筆記:從新手到專家的成長之路系列 第 12

[Day 12] 登入驗證 (一):Basic Auth

  • 分享至 

  • xImage
  •  

在大多數情況,後端 API 並不會隨便對外開放,需要有足夠的權限才可以訪問,有的只要一般會員就好,有的則是要管理員層級才可以。而辨識身分的方法,就是我們接下來要討論的主題。

一般操作流程

讓我們來快速回想一下平常是怎麼登入的?

最常見的流程是:

  1. 進入登入畫面
  2. 輸入帳號、密碼
  3. 送出

接下來的一段時間內,就可以自由地使用會員功能,直到我們登出,或是系統設定的登入時間結束

時間長短往往取決於對安全的要求,尤其是跟金錢有關的服務,例如:網路銀行

那為什麼後端可以知道我們是會員呢?

這是因為最一開始我們有先註冊過,後端資料庫內有儲存我們的帳號與密碼的相關資訊,因此,只要比對資料庫,就知道這次的登入是否成功。

注意!資料庫是不能直接儲存密碼的,它需要透過一些轉換方式 (後面會介紹) 轉換過後,才能儲存進資料庫。這麼做是為了避免資料外洩時,用戶的密碼直接赤裸裸地出現在大家面前。

這邊來看一下極簡單的例子

# main.py
from fastapi import FastAPI, Form

app = FastAPI()

fake_user_db = [
    {
        "username": "ithome",
        "password": "secret"
    }, {
        "username": "ironman",
        "password": "password"
    }
]

@app.post("/login")
async def login(username: str = Form(), password: str = Form()):
    is_user = False
    for user in fake_user_db:
        if username == user["username"] and password == user["password"]:
            is_user = True
            break

    return {"is_user": is_user}

輸入錯誤的帳密,後端就會告訴你失敗

輸入正確的才會回傳成功

現在這個範例可以說是漏洞百出,只是為了快速展示概念用的,請大家不要真的拿去實作

再往下思考一下,如果每次都要在 Request body 攜帶帳號密碼,實在很不方便,同時也限制了 HTTP method。因此,後來討論出來的公認做法,就是在 Request header 夾帶身分認證的資訊。

會使用「身分認證的資訊」這個詞的原因是,並不是所有方法都是直接使用帳號、密碼

怎麼在 Header 夾帶身分認證資訊?

既然是公認的作法,自然也有標準的格式,也就是

Authorization: <type> <credentials>

其中 type 是指認證方式,而 credentials 則是該認證格式的身分資訊,這邊介紹一下兩種常見的認證格式

Basic Authentication

是最基本的格式,作法就是將帳號與密碼用 : 接起來後,用 Base64 編碼 (encode) 轉換,最後在 Header 帶入

Authorization: Basic <credentials>

舉個例子:
假設帳號是 ithome,密碼是 secret,那接起來就是 ithome:secret,再經過 Base64 encode 得到 aXRob21lOnNlY3JldA==,因此在 Header 帶入

Authorization: Basic aXRob21lOnNlY3JldA==

後端只要再 Base64 編碼轉換回去就可以得到帳號、密碼了

有很多免費工具可以幫我們做 Base64 轉換,例如 這個網站

JSON Web Tokens (JWT)

今天先看一下範例就好,細節明天再介紹~ XD

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.tImCzvIkqaNmGB5mMAG1DZRnZO56sjoYO5nU2YUdRK4

如何在 FastAPI 實作 Basic Authentication

知道概念後,其實要土法煉鋼自己寫出 Basic Authentication (以下簡稱 Basic Auth) 也不難,但重複造輪子的事還是能避免就避免,時間可是很寶貴的。

# main.py
from typing import Annotated

from fastapi import Depends, FastAPI
from fastapi.security import HTTPBasic, HTTPBasicCredentials

app = FastAPI()

security = HTTPBasic()

@app.get("/users/me")
def read_current_user(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
    return {"username": credentials.username, "password": credentials.password}

注意這個範例只有最基本的 Basic Auth,也就是說,它只要求使用者提供帳號、密碼,並沒有做驗證

接著使用 Postman 進行測試。

Postman 有一個很貼心的功能,它把登入驗證的部份獨立到 Auth 頁籤下,我們只要選擇好對應的 type,並輸入對應的資訊,它就會幫我們把它做對應的轉換並放入 Header,不需要再自己去做 Base64 encode。

之後在 UsernamePassword 輸入帳號、密碼,並送出。

就會收到後端回傳的帳號、密碼,代表 FastAPI 真的有幫我們解析出帳號與密碼。

如果沒有在 Header 放登入資訊,則會收到 401 錯誤。

如同上方所說,這個範例是沒有驗證帳號、密碼的,官網有更完整的範例,大家有興趣的話可以去看看~

Swagger UI

接下來讓我們看看 API 文件的變化,此時文件右側有一個 Authorize 按鈕。

點擊之後就會看到一個彈出視窗,要求輸入帳號、密碼

接著我們隨便輸入一組帳號、密碼,並按下 Autherize 按鈕

這樣我們就進入登入狀態了,後續在這份文件上測試 API 都會自動帶有 Basic Auth 的 Header

回到 API 文件就會發現右邊的鎖的 icon 發生變化了~

這是測試剛剛的 API 的結果

但如果不按右上的 Autherize 按鈕,就直接去測試 API,它也會出現彈出視窗要求輸入帳號、密碼

重點回顧

今天我們主要介紹了 Basic Auth 的概念與做法,明天會繼續介紹 JWT 的那一長串的身分資訊是怎麼來的?以及要怎麼在 FastAPI 實作 JWT 的登入驗證?


上一篇
[Day 11] Middleware 與 CORS
下一篇
[Day 13] 登入驗證 (二):JWT
系列文
FastAPI 開發筆記:從新手到專家的成長之路30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言